﻿// ReSharper disable CppClangTidyModernizeUseNodiscard
#pragma once

#include "Material.h"
#include "Texture2D.h"
#include "Path.h"
#include "ShaderProgram.h"

struct FTextureInfo
{
	// 贴图对应在Shader 中的槽位
	int32_t Index = 0;
	std::string TypeName;
	std::filesystem::path TexturePath;
};

class FMeshMaterialBase
	: public FMaterial
{
public:

	void SetTextureInfoArr(std::vector<FTextureInfo>&& Infos)
	{
		TextureInfos = std::move(Infos);
	}

protected:


	std::vector<FTextureInfo> TextureInfos;

};


class FMeshSimpleMaterial final
	: public FMeshMaterialBase
{
private:

	std::shared_ptr<FTexture2D> Texture;

public:



	virtual bool Init() override
	{
		InitShaderWayType = EInitShaderWayType::SrcCode;

		if (!FMaterial::Init())
		{
			return false;
		}

		Shader->SetLightingModel(ELightingModel::None);


		Texture = FTextureBuild{}.Build(TextureInfos[0].TexturePath);

		Shader->Use();

		Shader->SetInt("texture_diffuse1", 0);

		return true;
	}

	virtual void Use() override
	{
		FMaterial::Use();

		Texture->BindTextureUnit(0);
	}

	const std::string& GetVsSrcCode() const override
	{
		static std::string SrcCode = R"(
			#version 330 core
				layout (location = 0) in vec3 aPos;
			layout (location = 2) in vec2 aTexCoords;

			out vec2 TexCoords;

			uniform mat4 projection;
			uniform mat4 view;
			uniform mat4 model;

			void main()
			{
				TexCoords = aTexCoords;
				gl_Position = projection * view * model * vec4(aPos, 1.0f); 
			}
		)";
		return SrcCode;
	}
	const std::string& GetFsSrcCode() const override
	{
		static std::string SrcCode = R"(
			#version 330 core
			out vec4 FragColor;
			
			in vec2 TexCoords;
			
			uniform sampler2D texture_diffuse1;
			
			void main()
			{
			    FragColor = texture(texture_diffuse1, TexCoords);
			}
		)";
		return SrcCode;
	}

};


class FMeshMaterialDiffuse final
	: public FMeshMaterialBase
{
public:

	std::filesystem::path GetVsSrcPath() const override
	{
		return FPath::BuildShaderPath("Mesh/shader.vs");
	}

	std::filesystem::path GetFsSrcPath() const override
	{
		return FPath::BuildShaderPath("Mesh/shader_diffuse.fs");
	}

private:

	std::vector<std::shared_ptr<FTexture2D>> TextureArray;


public:



	virtual bool Init() override
	{
		InitShaderWayType = EInitShaderWayType::File;

		if (!FMaterial::Init())
		{
			return false;
		}

		Shader->SetLightingModel(ELightingModel::Diffuse);

		for (const auto& TextureInfo : TextureInfos)
		{
			const auto Texture = FTextureBuild{}.Build(TextureInfo.TexturePath);
			TextureArray.push_back(Texture);
		} 

		Shader->Use();

		Shader->SetInt("material.diffuse", 0);

		return true;
	}

	virtual void Use() override
	{
		FMaterial::Use();
		
		if (!TextureArray.empty())
		{
			TextureArray[0]->BindTextureUnit(0);
		}
	}
};


class FMeshMaterialDiffuseShininessr final
	: public FMeshMaterialBase
{
public:

	std::filesystem::path GetVsSrcPath() const override
	{
		return FPath::BuildShaderPath("Mesh/shader.vs");
	}

	std::filesystem::path GetFsSrcPath() const override
	{
		return FPath::BuildShaderPath("Mesh/shade_diffuse_shininessr.fs");
	}

private:

	std::shared_ptr<FTexture2D> TextureDiffuse;

	std::shared_ptr<FTexture2D> TextureSpecular;

public:

	virtual bool Init() override
	{
		InitShaderWayType = EInitShaderWayType::File;

		if (!FMaterial::Init())
		{
			return false;
		}

		for (const auto& TextureInfo : TextureInfos)
		{
			const auto Texture = FTextureBuild{}.Build(TextureInfo.TexturePath);
			if (TextureInfo.TypeName == "textureDiffuse")
			{
				TextureDiffuse = Texture;
			}
			else
			{
				TextureSpecular = Texture;
			}
		} 

		Shader->Use();


		Shader->SetFloat("material.shininess", 32.0f);
		Shader->SetInt("material.diffuse", 0);
		Shader->SetInt("material.specular", 1);

		return true;
	}

	virtual void Use() override
	{
		FMaterial::Use();

		TextureDiffuse->BindTextureUnit(0);
		TextureSpecular->BindTextureUnit(1);
	}
};